﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Pokemon
{
    /// <summary>
    /// Grid is a array that has Blank in each cell
    /// </summary>
    public class Grid: DrawableGameComponent, ITappedInner
    {
        #region Attribute

        private Vector2 _size;

        private Point _position;

        private int _rows;

        private int _cols;

        private Blank[,] _blanks;
        
        private readonly string _fileName;

        private GameScreen _owner;

        private bool _showGridLine;

        private PushalbeBlank _selectedBlank;

        private bool _isPushableGrid;

        //2 thuoc tinh de luu giu khoang cach giua 2 blank trong 1 grid

        private int _distanceX; //Khoang cach giua 2 blank trong cung 1 hang

        private int _distanceY; //Khoang cach giua 2 blank trong cung 1 cot
#endregion

        #region Properties
        public Point Position
        {
            get { return _position; }
            set { _position = value; }
        }

        public Vector2 Size
        {
            get { return _size; }
            set { _size = value; }
        }

        public GameScreen Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }

        public int Rows
        {
            get { return _rows; }
        }

        public int Cols
        {
            get { return _cols; }
        }

        public PushalbeBlank SelectedBlank
        {
            get { return _selectedBlank; }
            set { _selectedBlank = value; }
        }

        public Blank[,] Blanks
        {
            get { return _blanks; }
            set { _blanks = value; }
        }

        #endregion

        #region Draw and Update

        public override void Update(GameTime gameTime)
        {
            for (int i = 0; i < Rows; i++)
            {
                for (int j = 0; j < Cols; j++)
                {
                    Blanks[i, j].Update(gameTime);
                }                
            }            
        }

        public override void Draw(GameTime gameTime)
        {          
            if (_showGridLine)
            {
                for (int i = 0; i < Rows; i++)
                {
                    for (int j = 0; j < Cols; j++)
                    {
                        Blanks[i, j].Draw(gameTime);
                        Blanks[i, j].DrawBorder();
                    }
                }
            }
            else
            {
                for (int i = 0; i < Rows; i++)
                {
                    for (int j = 0; j < Cols; j++)
                    {
                        Blanks[i, j].Draw(gameTime);
                    }
                }    
            }                        
        }

#endregion        

        #region Support Method

        public void Add(int row, int col, Unit unit)
        {
            Blanks[row, col].Add(unit);
        }

        public void GetNeighborBlank(Blank srcBlank, out Blank leftBlank, out Blank rightBlank)
        {
            leftBlank = rightBlank = null;
            for (var i = 0; i < Rows; i++)
                for (var j = 0; j < Cols; j++)
                    if (Blanks[i, j] == srcBlank)
                    {
                        if (j > 0)
                            leftBlank = Blanks[i, j - 1];

                        if (j < Cols - 1)
                            rightBlank = Blanks[i, j + 1];
                    }
        }

        public void AimBlank(Vector2 pos, Vector2 size, out int row, out int col)
        {
            row = (int)((pos.Y - _position.Y) / (size.Y + _distanceY));
            col = (int)((pos.X - _position.X) / (size.X + _distanceX));
            Rectangle rect = new Rectangle((int)Blanks[row, col].Position.X, (int)Blanks[row, col].Position.Y,
                                           (int)Blanks[row, col].Size.X, (int)Blanks[row, col].Size.Y);
            if (rect.Contains(new Point((int)pos.X, (int)pos.Y)) == false)
            {
                row = col = -1;
            }
        }

        public Blank GetBlank(int row, int col)
        {
            if (row < 0 || col < 0 || row >= Rows || col >= Cols)
                return null;
            return Blanks[row, col];
        }

        public void ShowGridLine()
        {
            _showGridLine = true;
        }

        public void HideGridLine()
        {
            _showGridLine = false;
        }

        public bool IsPushed()
        {
            return _selectedBlank == null;
        }

        #endregion

        public Grid(Game game, GameScreen owner, string fileName)
            : base(game)
        {
            _fileName = fileName;
            Owner = owner;            
            Initialize();
        }

        #region Initialize

        public override void Initialize()
        {
            XDocument doc = XDocument.Load(_fileName);
            XName name = XName.Get(@"Rows");
            var width = 0;
            var height = 0;
            if (doc.Document != null)
            {
                var definitions = doc.Document.Descendants(name);
                Int32 value = Int32.Parse((definitions.First()).Value);
                _rows = value;
                name = XName.Get(@"Cols");
                definitions = doc.Document.Descendants(name);
                value = Int32.Parse((definitions.First()).Value);
                _cols = value;
                name = XName.Get(@"Position");
                definitions = doc.Document.Descendants(name);
                var xAttribute = definitions.First().Attribute(@"X");
                if (xAttribute != null)
                {
                    var attribute = definitions.First().Attribute(@"Y");
                    if (attribute != null)
                        Position = new Point(Int32.Parse(xAttribute.Value),
                                             Int32.Parse(attribute.Value));
                }

                name = XName.Get(@"Blank");
                definitions = doc.Document.Descendants(name);
                xAttribute = definitions.First().Attribute(@"Width");
                if (xAttribute != null)
                {
                    var attribute = definitions.First().Attribute(@"Height");
                    if (attribute != null)
                    {
                        width = Int32.Parse(xAttribute.Value);
                        height = Int32.Parse(attribute.Value);
                    }
                }

                name = XName.Get(@"Distance");
                definitions = doc.Document.Descendants(name);
                xAttribute = definitions.First().Attribute(@"X");
                if (xAttribute != null) _distanceX = Int32.Parse(xAttribute.Value);
                xAttribute = definitions.First().Attribute(@"Y");
                if (xAttribute != null) _distanceY = Int32.Parse(xAttribute.Value);

                _size = new Vector2(width*_cols + _distanceX*(_cols - 1), height*_rows + _distanceY*(_rows - 1));

                name = XName.Get(@"IsPushable");
                definitions = doc.Document.Descendants(name);
                _isPushableGrid = bool.Parse(definitions.First().Value);

                if (_isPushableGrid)
                {                    
                    Pushed += BlankPush;

                    Blanks = new PushalbeBlank[Rows,Cols];
                    for (var i = 0; i < Rows; i++)
                        for (var j = 0; j < Cols; j++)
                            Blanks[i, j] = new PushalbeBlank(_owner.CurrGame,
                                                              new Vector2(j*(width + _distanceX) + _position.X,
                                                                          i*(height + _distanceY) + _position.Y),
                                                              new Vector2(width, height), this);

                }
                else
                {
                    Blanks = new Blank[Rows,Cols];
                    for (var i = 0; i < Rows; i++)
                    {
                        List<Blank> t = new List<Blank>();
                        for (var j = 0; j < Cols; j++)
                        {
                            Blanks[i, j] = new Blank(_owner.CurrGame,
                                                      new Vector2(j*(width + _distanceX) + _position.X,
                                                                  i*(height + _distanceY) + _position.Y),
                                                      new Vector2(width, height), this);
                            t.Add(Blanks[i, j]);
                        }
                        Lane l = new Lane(t);
                        LaneManager.Add(l);
                    }

                }

                name = XName.Get(@"Card");
                definitions = doc.Document.Descendants(name);
                var index = 0;
                string sourcePath = "";
                foreach (var definition in definitions)
                {
                    var b = definition.Attribute(@"Name");
                    if (b != null)
                    {
                        string type = b.Value;
                        var attribute = definition.Attribute(@"PathDef");
                        if (attribute != null)
                            sourcePath = attribute.Value;
                        Blank blank = GetBlank(index% _rows, index/ _rows);
                        Card c = new Card(Game, sourcePath, this, blank.Position, blank.Size);
                        blank.Add(c);
                        index++;
                        switch (type)
                        {
                            case "Pikachu":
                                Pikachu.Avatar = c;
                                break;
                            case "Blaziken":
                                Blaziken.Avatar = c;
                                break;
                            case "Charizard":
                                Charizard.Avatar = c;
                                break;
                            case "Blastoise":
                                Blastoise.Avatar = c;
                                break;
                            case "Venusaur":
                                Venusaur.Avatar = c;
                                break;
                            case "Dragonite":
                                Dragonite.Avatar = c;
                                break;
                        }

                    }
                }
            }

            base.Initialize();
        }

#endregion

        #region ITappedInner Method

        /// <summary>
        /// Kiem tra xem diem p co nam trong pham vi kiem soat cua Grid hay ko, neu co thì goi den Blank nao dang dc nhan de xu ly
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool CheckTapped(Point p)
        {
            Rectangle rect = new Rectangle(_position.X, _position.Y, (int)_size.X, (int)_size.Y);
            var isTapped = rect.Contains(p);
            if (isTapped)
            {
                Vector2 size = Blanks[0, 0].Size;
                int r, c;
                AimBlank(new Vector2(p.X, p.Y), size, out r, out c);
                //var r = (int)((p.Y - _position.Y)/size.Y);
                //var c = (int)((p.X - _position.X)/size.X);    
                if (r == -1)
                    return false;
                Blanks[r, c].OnClicked(p);
            }
            return isTapped;
        }          
        
#endregion

        #region Event: Raise when user tap on PushableBlank
        public delegate void BlankPushed();

        public event BlankPushed Pushed;

        public void OnPush()
        {
            BlankPushed handler = Pushed;
            if (handler != null) handler();
        }

        public void BlankPush()
        {
            SelectedBlank = null;
        }
#endregion

        public bool InsertCard(Card c)
        {
            for (var i = 0; i < _rows; i++)
            {
                for (var j = 0; j < _cols; j++)
                {
                    foreach (var component in _blanks[i, j].Components)
                    {
                        if (component is Card)
                            continue;
                    }
                    _blanks[i, j].Add(c);
                    return true;
                }
            }
            return false;
        }

        public Blank FindBlank(Point p)
        {
            Rectangle rect = new Rectangle(_position.X, _position.Y, (int)_size.X, (int)_size.Y);
            if (!rect.Contains(p))
                return null;
            
            foreach (var blank in _blanks)
            {
                if (blank.IsContainPoint(p))
                    return blank;
            }
            return null;
        }

    }
}
